/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/

//#include <unistd.h>
#include <string>
#include <vector>
#include <math.h>


#include "GL/glut.h"

#include "picking.hpp"
#include "objects.hpp"
#include "controls.hpp"

#include "ownutils.h"

#define ANGLE_INCR 2.0;
#define DIST_INCR 0.1

#define Z_INITIAL -1.5
#define Y_INITIAL 1.5
#define X_INITIAL 0
#define PAN_INITIAL 0
#define TILT_INITIAL 20

#define DegToRad(x) (( x )*M_PI/180.0)
 
#define RadToDeg(x) (( x )*180.0/M_PI)

static float x,y,z,pan,tilt;

CGLPickingContext c3d;
cube cube1, cube2;

std::vector<CGLPickable*> objects;

void ResetView()
{
  z=Z_INITIAL;
  y=Y_INITIAL;
  x=X_INITIAL;
  pan=PAN_INITIAL;
  tilt=TILT_INITIAL;
}

void SetView()
{
  double m[16];
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();

  glRotatef(pan,0,1,0);
  glRotatef(tilt,cos(DegToRad(pan)),0,sin(DegToRad(pan)));
  glTranslatef(x,-y,z);

  glGetDoublev(GL_MODELVIEW_MATRIX,m);
  c3d.SetModelviewMatrix(m);

}


/********************* glut callbacks **************************/

void DisplayFunc()
{
  SetView();
  
  glBegin(GL_LINES);
    //x
  glColor3f(1,0,0);
  glVertex3f(0,0,0);
  glVertex3f(1,0,0);
    //z
  glColor3f(0,0,1);
  glVertex3f(0,0,0);
  glVertex3f(0,1,0);
    //y
  glColor3f(0,1,0);
  glVertex3f(0,0,0);
  glVertex3f(0,0,-1);
  glEnd();
  
  /*
  cube1.Draw();
  cube2.Draw();
  */
  
  for (std::vector<CGLPickable*>::iterator i=objects.begin(); i!=objects.end(); i++)
    (*i)->Draw();
  
  glutSwapBuffers();
  
}

void IdleFunc()
{
  glutPostRedisplay();
  ownSleep_ms(5);
}

void KeyboardFunc(int key, int mouse_x, int mouse_y)
{
  switch (key)
    {
      case GLUT_KEY_UP :
      //z++;
        x=x-sin(DegToRad(pan))*DIST_INCR;
        z=z+cos(DegToRad(pan))*DIST_INCR;
        break;
      case GLUT_KEY_DOWN:
        //z--;
        x=x+sin(DegToRad(pan))*DIST_INCR;
        z=z-cos(DegToRad(pan))*DIST_INCR;
        break;
      case GLUT_KEY_RIGHT:
        pan=pan+ANGLE_INCR;
        break;
      case GLUT_KEY_LEFT:
        pan=pan-ANGLE_INCR;
        break;
      case GLUT_KEY_PAGE_UP:
        y=y+DIST_INCR/2;
        break;
      case GLUT_KEY_PAGE_DOWN:
        y=y-DIST_INCR/2;
        break;
      case GLUT_KEY_HOME:
        tilt=tilt-ANGLE_INCR;
        break;
      case GLUT_KEY_END:
        tilt=tilt+ANGLE_INCR;
        break;
      case GLUT_KEY_INSERT:
        tilt=0;
        break;
    }
}

void glutMouseFunc(int button, int state, int x, int y)
{
  if (state==GLUT_DOWN)
  {
    c3d.OnMouseDown(x,y,1);
  } else if (state == GLUT_UP) {
    c3d.OnMouseUp(x,y);
  }
  
}

void glutMotionFunc(int x, int y)
{
  c3d.OnMouseMove(x,y);
}

void glutPassiveMotionFunc(int x, int y)
{
  c3d.OnMouseMove(x,y);
}

void ReshapeFunc(int w, int h)
{
  double m[16];
  glMatrixMode(GL_PROJECTION);
  glViewport(0, 0, w, h);
  glLoadIdentity();
  gluPerspective(80,1.0*w/h,0.1,1000);
  
  glGetDoublev(GL_PROJECTION_MATRIX,m);
  c3d.SetProjectionMatrix(m);
  
}


/******************* main *******************/

int main(int argc, char *argv[])
{  
  
  glutInit(&argc,argv);
  glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
    
  ResetView();
/*
  cube1.x=0;
  cube1.y=0;
  cube1.z=0;
  
  cube2.x=1;
  cube2.y=1;
  cube2.z=-1;
  
  c3d.AddPickable(&cube1);
  
  c3d.AddPickable(&cube2);
*/
  
  for (int i=-10; i<10 ; i+=2)
  {
    for (int j=-10; j<10 ; j+=2)
    {
      cube *c=new cube();
      c->x=i;
      c->y=j;
      c->z=0;
      c3d.AddPickable(c);
      objects.push_back(c);
    }
  }
  
  for (int j=-10; j<10 ; j+=2)
  {
    torus *c=new torus();
    c->x=j;
    c->y=11;
    c->z=0;
    c3d.AddPickable(c);
    objects.push_back(c);
  }
  
  glutCreateWindow("Pick test in 3d");
  
  glClearColor(0,0,0,0);
  glEnable(GL_DEPTH_TEST);
  
  glutDisplayFunc(DisplayFunc);
  glutIdleFunc(IdleFunc);
  glutSpecialFunc(KeyboardFunc);
  glutReshapeFunc(ReshapeFunc);
  glutMouseFunc(glutMouseFunc);
  glutMotionFunc(glutMotionFunc);
  glutPassiveMotionFunc(glutPassiveMotionFunc);
  glutMainLoop();
}
